<?php
declare (strict_types = 1);

namespace app\service\basic\auth;

use Closure;
use think\App;
use think\exception\InvalidArgumentException;
use app\service\basic\auth\Factory as FactoryContract;

class AuthManager implements FactoryContract
{
    use CreatesUserProviders;

    protected $app;

    /**
     * The user resolver shared by various services.
     *
     * Determines the default user.
     *
     * @var Closure
     */
    protected $userResolver;

    /**
     * The array of created "drivers".
     *
     * @var array
     */
    protected $guards = [];

    public function __construct(App $app)
    {
        $this->app = $app;

        $this->userResolver = function ($guard = null) {
            return $this->guard($guard)->user();
        };
    }

    /**
     * Get the guard configuration.
     *
     * @param  string  $name
     * @return array
     */
    protected function getConfig($name)
    {
        return $this->app->config->get("auth.guards.{$name}");
    }

    /**
     * Get the default authentication driver name.
     *
     * @return string
     */
    public function getDefaultDriver()
    {
        return $this->app->config->get('auth.defaults.guard');
    }

    /**
     * Set the default authentication driver name.
     *
     * @param  string  $name
     * @return void
     */
    public function setDefaultDriver($name)
    {
        $this->app->config->set(['guard' => $name], 'auth.defaults');
    }

    /**
     * Attempt to get the guard from the local cache.
     *
     * @param null $name
     * @return mixed
     */
    public function guard($name = null)
    {
        $name = $name ?: $this->getDefaultDriver();

        return $this->guards[$name] ?? $this->guards[$name] = $this->resolve($name);
    }

    /**
     * Set the default guard driver the factory should serve.
     *
     * @param string $name
     */
    public function shouldUse($name)
    {
        $name = $name ?: $this->getDefaultDriver();

        $this->setDefaultDriver($name);

        $this->userResolver = function ($name = null) {
            return $this->guard($name)->user();
        };
    }

    /**
     * 返回驱动
     *
     * @param $name
     * @return mixed
     */
    protected function resolve($name)
    {
        $config = $this->getConfig($name);
        if (is_null($config)) {
            throw new InvalidArgumentException("Auth guard [{$name}] is not defined.");
        }

        $driverMethod = 'create'.ucfirst($config['driver']).'Driver';

        if (method_exists($this, $driverMethod)) {
            return $this->{$driverMethod}($name, $config);
        }

        throw new InvalidArgumentException("Auth driver [{$config['driver']}] for guard [{$name}] is not defined.");
    }

    /**
     * jwt
     *
     * @param $name
     * @param $config
     * @return JWTGuard
     */
    protected function createJwtDriver($name, $config)
    {
        return new JWTGuard($this->app->make('thans\jwt\JWTAuth'), $this->createUserProvider($config['provider']), $name);
    }

    /**
     * Dynamically call the default driver instance.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        return $this->guard()->{$method}(...$parameters);
    }
}
